/*
 * @Author: 晴鸢. 3013433857@qq.com
 * @Date: 2025-11-07 12:23:33
 * @LastEditors: 晴鸢. 3013433857@qq.com
 * @LastEditTime: 2025-11-15 12:19:55
 * @FilePath: /Learn/Webserver/HttpServer.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
/*
 * @Author: 晴鸢. 3013433857@qq.com
 * @Date: 2025-11-07 12:23:33
 * @LastEditors: 晴鸢. 3013433857@qq.com
 * @LastEditTime: 2025-11-13 13:03:22
 * @FilePath: /Learn/Webserver/HttpServer.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include<fstream>
#include<sstream>
#include<unordered_map>
#include"Socket.hpp"
#include"Log.hpp"

const std::string wwwroot="./wwwroot";//web根目录
const std::string sep="\r\n";
const std::string homeage="index.html";
const int defaultport=8080;
class HttpServer;
class ThreadData
{
public:
    ThreadData(int _sockfd,HttpServer*s)
        :sockfd(_sockfd)
        ,svr(s)
    {}
public:
    int sockfd;
    HttpServer *svr;
};

class HttpRequest
{
public:
    void Deserialize( std::string req)
    {
        while(true)
        {
            size_t pos=req.find(sep);
            if(pos==std::string::npos) break;
            std::string temp=req.substr(0,pos);
            if(temp.empty()) break;
            req_header.push_back(temp);
            req.erase(0,pos+sep.size());
        }
        text=req;
    }

    void Prase()
    {
        //字符串分割流
        std::stringstream ss(req_header[0]);
        ss>>method>>url>>http_version;//默认按空格，逗号分割  得到三个字段
        //处理得到资源文件
        file_path=wwwroot;// ./wwwroot
        if(url=="/"||url=="/index.html") 
        {
            file_path+='/';
            file_path+=homeage;
        }
        else file_path+=url;

        //获取文件后缀，以便后续处理
        auto pos=file_path.rfind(".");
        if(pos==std::string::npos) suffix=".html";//默认设置
        else suffix=file_path.substr(pos);

    }

    void Debugprint()
    {
        for(auto line:req_header)
        {
            std::cout<<line<<"\n\n";
        }
        std::cout<<"method:"<<method<<std::endl;
        std::cout<<"url:"<<url<<std::endl;
        std::cout<<"http_version:"<<http_version<<std::endl;
        std::cout<<"file_path:"<<file_path<<std::endl;
        std::cout<<text<<std::endl;
    }
public:
    std::vector<std::string> req_header;
    std::string text;

    std::string method;//http的请求方法
    std::string url;//存储资源的路径
    std::string http_version;//http协议的版本信息
    std::string file_path;
    std::string suffix;//表示文件后缀
};

class HttpServer
{
public:
    HttpServer(int port=defaultport)
       :_port(port)
    {
        content_type.insert({".html","text/html"});
        content_type.insert({".jpg","image/jpeg"});

    }

    bool Start()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        while(1)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd=_listensock.Accept(&clientip,&clientport);
            if(sockfd<0) continue;
            lg(Info,"get a new connect,sockfd:%d",sockfd);
            pthread_t tid;
            ThreadData*td=new ThreadData(sockfd,this);
            pthread_create(&tid,nullptr,ThreadRun,td);
        }
    }

    static std::string ReadHtmlContent(const std::string &htmlpath)
    {
        //图片为二进制文件，需用二进制的模式打开
        std::ifstream in(htmlpath,std::ios::binary);
        if(!in.is_open()) return "";
        
        in.seekg(0,std::ios_base::end);//把文件指针移动到文件末尾
        auto len=in.tellg();//获取此时文件指针相当于文件开头的偏移量(即文件大小)
        in.seekg(0,std::ios_base::beg);//把文件指针移动到文件开头

        std::string content;
        content.resize(len);

        in.read((char*)content.c_str(),content.size());
        in.close();
        return content;
        /*
        std::string line;
        std::string content;
        while(getline(in,line))
        {
            content+=line;
        }
        in.close();
        return content;
        */
    }

    std::string SuffixToDesc(const std::string &suffix)
    {
        auto iter=content_type.find(suffix);
        if(iter==content_type.end())
            return content_type[".html"];
        else return content_type[suffix];
    }

    void HandlerHttp(int sockfd)
    {
        char buffer[10240];
        ssize_t n=recv(sockfd,buffer,sizeof(buffer)-1,0);
        if(n>0)
        {
            buffer[n]='\0';
            std::cout<<buffer;//假设读到的为一个完整的独立的http请求
            
            HttpRequest req;
            req.Deserialize(buffer);
            req.Prase();
            //req.Debugprint();
            
            //返回响应
            std::string text = ReadHtmlContent(req.file_path);
            bool ok=1;
            if(text.empty())
            {
                ok=false;
                std::string err_html=wwwroot;
                err_html+='/';
                err_html+="err.html";
                text=ReadHtmlContent(err_html);
            }
            std::string response_line;
            if(ok)
                response_line="HTTP/1.0 200  OK\n";
            else
                response_line="HTTP/1.0 404 Not Found\n" ;
            std::string response_header="Content_Length:";
            response_header+=std::to_string(text.size());
            response_header+="\r\n";
            response_header+="Content-Type:";
            response_header+=SuffixToDesc(req.suffix);
            response_header+="\r\n";
            response_header+="Set-Cookie:name=TmT&&passward=123456";
            response_header+="\r\n";

            std::string blank_line="\n";

            std::string response=response_line;
            response+=response_header;
            response+=blank_line;//空行
            std::cout<<response<<std::endl;
            response+=text;

            send(sockfd,response.c_str(),response.size(),0);
            
            
        }
        close(sockfd);
    }

    static void* ThreadRun(void*args)
    {
        pthread_detach(pthread_self());
        ThreadData *td=static_cast<ThreadData*>(args);
        td->svr->HandlerHttp(td->sockfd);
        delete td;
        return nullptr;
    }

    ~HttpServer()
    {}
private:
    Sock _listensock;
    uint16_t _port;
    //content_type 对照表，按需添加即可
    std::unordered_map<std::string,std::string> content_type;
};